/*
 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011, 2012
 * 2014 Robert Lougher <rob@jamvm.org.uk>.
 *
 * This file is part of JamVM.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#include "jam.h"

static int VM_initing = TRUE;
extern void initialisePlatform();

unsigned long clampHeapLimit(long long limit) {
    long long int clamp = MAX(limit, MIN_MIN_HEAP);
    return (unsigned long)MIN(clamp, MAX_MAX_HEAP);
}

/* Setup default values for command line args */

void setDefaultInitArgs(InitArgs *args) {
    /* Long longs are used here because with PAE, a 32-bit
       machine can have more than 4GB of physical memory */
    // 这里之所以使用long long类型，是因为在PAE的情况下，32位机器的物理内存可以超过4GB。
    // 获取物理内容大小 内部实现是从系统配置中获取页的数量 * 页的大小 得到物理内存的大小
    long long phys_mem = nativePhysicalMemory();

    // 看名字是异步GC 这里设置为不支持
    args->asyncgc = FALSE;
    

    // TODO 这个是啥gc配置？
    // 好像是gc的信息打印 
    // -verbose:gc print out results of garbage collection
    //  :jni print out native method dynamic resolution
    // :class print out information about class loading, etc.
    args->verbosegc    = FALSE;
    // TODO 啥动态链接库 这个就是jni的配置啊？ 可以debug一下关于jni的
    args->verbosedll   = FALSE;
    // 这个应该就是class信息打印
    args->verboseclass = FALSE;

    // 难道上面的verbose 配置 都是jvm的 -verbose参数？有点像的，大概就是一个监控软件，意思是jamvm不支持这些监控？

    // TODO 跟踪jni 标志 我也不知道是个啥
    args->trace_jni_sigs = FALSE;
    
    // TODO 字面意思 紧凑的指定（紧凑）
    args->compact_specified = FALSE;

    // class路径
    args->classpath  = NULL;
    // TODO 这个是干嘛的 下面这些都不知道
    args->bootpath   = NULL;
    args->bootpath_p = NULL;
    args->bootpath_a = NULL;
    args->bootpath_c = NULL;
    args->bootpath_v = NULL;

    // java栈大小 默认是256kb
    // 天哪 终于看到熟悉的东西了
    args->java_stack = DEFAULT_STACK;
    // 最大的堆容量 
    // 如果物理内存为0 则使用默认定义 256*MB
    args->max_heap   = phys_mem == 0 ? DEFAULT_MAX_HEAP
                                     : clampHeapLimit(phys_mem/4); // 否则使用1/4的物理内存的大小 方法里面最低不会小于16mb 最大不会超过1024mb
    // 最小的堆容量 默认16MB
    args->min_heap   = phys_mem == 0 ? DEFAULT_MIN_HEAP
                                     : clampHeapLimit(phys_mem/64); // 

    // TODO 属性数量？
    args->props_count = 0;

    // C 库函数 int vfprintf(FILE *stream, const char *format, va_list arg) 使用参数列表发送格式化输出到流 stream 中。
    args->vfprintf = vfprintf;
    // C 库函数 void abort(void) 中止程序执行，直接从调用的地方跳出。
    args->abort    = abort;
    // C 库函数 void exit(int status) 立即终止调用进程。任何属于该进程的打开的文件描述符都会被关闭，该进程的子进程由进程 1 继承，初始化，且会向父进程发送一个 SIGCHLD 信号。
    // status -- 返回给父进程的状态值。
    args->exit     = exit;

#ifdef INLINING
    args->replication_threshold = 10;
    args->profile_threshold     = 10;
    args->branch_patching_dup   = TRUE;
    args->branch_patching       = TRUE;
    args->print_codestats       = FALSE;
    args->join_blocks           = TRUE;
    args->profiling             = TRUE;
    args->codemem               = args->max_heap/4;
#endif

#ifdef HAVE_PROFILE_STUBS
    args->dump_stubs_profiles   = FALSE;
#endif
}

int VMInitialising() {
    return VM_initing;
}

/**
 * 使用初始化配置进行虚拟机的初始化
 * 
 **/
int initVM(InitArgs *args) {
    int status;

    /* Perform platform dependent initialisation 执行依赖于平台的初始化 */
    initialisePlatform();

    /* Initialise the VM modules -- ordering is important! 初始化VM模块 -- 顺序很重要! */

    status = 
            // 初始化钩子 主要是一个打印的c库函数vfprintf 和exit函数
            initialiseHooks(args) &&
            // 给properties.c的properties赋值 顺便获取class lib java home的地址
             initialiseProperties(args) &&
             // 初始化分配 对齐堆基（？） 初始自由列表 还有初始化GC锁等操作
             initialiseAlloc(args) &&
             // 初始化线程阶段 里面根据初始化参数初始化了线程相关的（废话）
             initialiseThreadStage1(args) &&
             // 初始化UTF-8？里面只做了一件事 把utf-8.c里面的hash_table初始化了（分配内存）
             initialiseUtf8() &&
             // 初始化 符号？ 符号是啥 
             // 这个好像是初始化关键字 里面有很多class的关键字 
             // 不过用处是啥 调试的时候看看 到底用来干嘛的
             initialiseSymbol() &&
             // 初始化class阶段 设置classpath、boot classpath，注册了java.lang.Class 还有一些特殊的class注册
             initialiseClassStage1(args) &&
             // dll 就是jni 拿这个方法就是初始化jni相关的东西 主要是一个hash表、一个jni-signatures文件还有boot_dll_path以及jni的日志打印开关
             initialiseDll(args) &&
             // 看名字就知道，监控相关的 内部就是初始化了一个hash表 应该是给监控用的hash表
             initialiseMonitor() &&
             // 看名字是初始化String，String为啥要初始化啊 通过查看源码发现 先是获取String这个class（这一步里面进行了查找、装载、链接）
             // 然后把String的value字段的偏移量找到 赋值给string.c中的value_offset，然后注册引用
             initialiseString() &&
             // 看名字是初始化异常 内部把虚拟机需要的异常给初始化了 所谓的初始化就和查找、装载、链接等等操作
             // 还有一些是和gnuclasspath或者jdk相关的操作
             initialiseException() &&
             // 看名字是初始化本地方法 调用了具体的class lib的方法做的处理
             // TODO 不是很明白是干嘛 看名字以为是把所有的class的natives给注册了，但其实不是
             initialiseNatives() &&
             // 初始化访问 用了具体的class lib的方法做的处理 跟上面那个一样
             // TODO 干嘛用的
             initialiseAccess() &&
             // 初始化帧 调用了具体的class lib的方法做的处理
             // TODO 干嘛用的
             initialiseFrame() &&
             // 嗯？？？？？ 这个跟dll啥区别啊
             // 先是初始化了全局引用表 然后调用了具体的class lib的方法做的处理
             // TODO 不是很懂
             initialiseJNI() &&
             // 初始化拦截器 
             // TODO 拦截器是啥
             // 这个里面的代码很深，可以仔细看看
             initialiseInterpreter(args) &&
             // 初始化class阶段2 里面做了个填充大小的判断 然后初始化了Class这个类 这个初始化应该就是JVM里面加载类这个初始化的过程
             initialiseClassStage2() &&
             // 初始化线程相关的操作 里面做了很多操作
             // TODO 方法里面做的事情还不是很懂，可以调试一下
             initialiseThreadStage2(args) &&
             // 哦吼，初始化GC~
             initialiseGC(args);

    VM_initing = FALSE;
    return status;
}

unsigned long parseMemValue(char *str) {
    char *end;
    unsigned long n = strtol(str, &end, 0);

    switch(end[0]) {
        case '\0':
            break;

        case 'G': case 'g':
            n *= KB;

        case 'M': case 'm':
            n *= KB;

        case 'K': case 'k':
            n *= KB;

            if(end[1] == '\0')
                break;

        default:
             n = 0;
    } 

    return n;
}

void optError(InitArgs *args, const char *fmt, ...) {
    va_list ap;

    va_start(ap, fmt);
    (*args->vfprintf)(stderr, fmt, ap);
    va_end(ap);
}

typedef struct compat_options {
    char *option;
    int flags;
} CompatOptions;

#define OPT_ARG   1
#define OPT_NOARG 2

static CompatOptions compat[] = {
    {"-XX",                      OPT_ARG},
    {"-Xloggc",                  OPT_ARG},
    {"-Xshare",                  OPT_ARG},
    {"-Xverify",                 OPT_ARG},
    {"-esa",                     OPT_NOARG},
    {"-dsa",                     OPT_NOARG},
    {"-Xrs",                     OPT_NOARG},
    {"-Xint",                    OPT_NOARG},
    {"-Xprof",                   OPT_NOARG},
    {"-Xcomp",                   OPT_NOARG},
    {"-Xbatch",                  OPT_NOARG},
    {"-Xmixed",                  OPT_NOARG},
    {"-Xincgc",                  OPT_NOARG},
    {"-Xcheck:jni",              OPT_NOARG},
    {"-Xnoclassgc",              OPT_NOARG},
    {"-enablesystemassertions",  OPT_NOARG},
    {"-disablesystemassertions", OPT_NOARG},
    {"-ea",                      OPT_NOARG | OPT_ARG},
    {"-da",                      OPT_NOARG | OPT_ARG},
    {"-enableassertions",        OPT_NOARG | OPT_ARG},
    {"-disableassertions",       OPT_NOARG | OPT_ARG},
    {NULL, 0}
};

/**
 * 
 * 解析命令行参数
 *  *string 参数
 *  *args 初始化参数结构体
 *  is_jni 是否是jni
 **/
int parseCommonOpts(char *string, InitArgs *args, int is_jni) {
    // 默认是成功的状态
    int status = OPT_OK;

    // strcmp函数是string compare(字符串比较)的缩写，用于比较两个字符串并根据比较结果返回整数。
    if(strcmp(string, "-Xasyncgc") == 0)
        // 配置了异步GC的参数？
        args->asyncgc = TRUE;
    // strncmp函数为字符串比较函数，字符串大小的比较是以ASCII 码表上的顺序来决定，此顺序亦为字符的值。
    // 其函数声明为int strncmp ( const char * str1, const char * str2, size_t n );功能是把 str1 和 str2 进行比较，最多比较前 n 个字节
    else if(strncmp(string, "-Xms", 4) == 0 ||
            (!is_jni && strncmp(string, "-ms", 3) == 0)) {
        // 如果匹配则标识当前参数是配置最小堆内存的
        // 获取参数对应的值
        char *value = string + (string[1] == 'm' ? 3 : 4);
        // 解析一下 g k m之类的
        args->min_heap = parseMemValue(value);
        
        // 校验是不是小于最小的堆配置了 默认是4kb
        if(args->min_heap < MIN_HEAP) {
            optError(args, "Invalid minimum heap size: %s (min %dK)\n",
                     string, MIN_HEAP/KB);
            // 标记状态
            status = OPT_ERROR;
        }

    } else if(strncmp(string, "-Xmx", 4) == 0 ||
              (!is_jni && strncmp(string, "-mx", 3) == 0)) {
        // 配置最大堆内存
        // 跟最小一样的套路
        char *value = string + (string[1] == 'm' ? 3 : 4);
        args->max_heap = parseMemValue(value);

        if(args->max_heap < MIN_HEAP) {
            optError(args, "Invalid maximum heap size: %s (min is %dK)\n",
                     string, MIN_HEAP/KB);
            status = OPT_ERROR;
        }

    } else if(strncmp(string, "-Xss", 4) == 0 ||
              (!is_jni && strncmp(string, "-ss", 3) == 0)) {
        // 配置java栈的大小
        char *value = string + (string[1] == 'm' ? 3 : 4);
        args->java_stack = parseMemValue(value);
        // java栈最少2kb 默认不配置是256kb
        if(args->java_stack < MIN_STACK) {
            optError(args, "Invalid Java stack size: %s (min is %dK)\n",
                     string, MIN_STACK/KB);
            status = OPT_ERROR;
        }

    } else if(strncmp(string, "-D", 2) == 0) {
        // C 库函数 char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。
        // 获取-D参数后面的key
        char *key = strcpy(sysMalloc(strlen(string + 2) + 1), string + 2);
        char *pntr;

        for(pntr = key; *pntr && (*pntr != '='); pntr++);
        if(*pntr)
            *pntr++ = '\0';
        
        // 大概就是把-D后面的参数放到commandline_props数组里面
        args->commandline_props[args->props_count].key = key;
        args->commandline_props[args->props_count++].value = pntr;

    } else if(strncmp(string, "-Xbootclasspath:", 16) == 0) {
        //  java -Xbootclasspath/a:/usrhome/thirdlib.jar: -jar yourJarExe.jar

        // -Xbootclasspath:     完全取代基本核心的Java class 搜索路径.不常用,否则要重新写所有Java 核心class
        args->bootpath = string + 16;
        args->bootpath_p = args->bootpath_a = NULL;

    } else if(strncmp(string, "-Xbootclasspath/a:", 18) == 0) {
        // -Xbootclasspath/a: 后缀在核心class搜索路径后面.常用!!
        args->bootpath_a = string + 18;

    } else if(strncmp(string, "-Xbootclasspath/p:", 18) == 0) {
        // -Xbootclasspath/p: 前缀在核心class搜索路径前面.不常用,避免引起不必要的冲突.
        args->bootpath_p = string + 18;

    } else if(strcmp(string, "-Xnocompact") == 0) {
        // -Xnocompact 不启用压缩
        args->compact_specified = TRUE;
        args->do_compact = FALSE;

    } else if(strcmp(string, "-Xcompactalways") == 0) {
        // 启用压缩
        args->compact_specified = args->do_compact = TRUE;

    } else if(strcmp(string, "-Xtracejnisigs") == 0) {
        // 是否启用追踪jni
        args->trace_jni_sigs = TRUE;
#ifdef INLINING //  翻译过来是内衬  不知道啥意思
    } else if(strcmp(string, "-Xnoinlining") == 0) {
        /* Turning inlining off is equivalent to setting
           code memory to zero 
           关闭内联相当于将代码内存设置为零 */
        args->codemem = 0;

    } else if(strcmp(string, "-Xnoprofiling") == 0) {
        // TODO 这个是啥
        args->profiling = FALSE;

    } else if(strcmp(string, "-Xnopatching") == 0) {
        // TODO 这个是啥 分支补丁
        args->branch_patching = FALSE;

    } else if(strcmp(string, "-Xnopatchingdup") == 0) {
        // 不打补丁
        args->branch_patching_dup = FALSE;

    } else if(strcmp(string, "-Xnojoinblocks") == 0) {
        // TODO 无连接块 干嘛用的？
        args->join_blocks = FALSE;

    } else if(strcmp(string, "-Xcodestats") == 0) {
        // TODO 这个应该是代码状态 干嘛用的？
        args->print_codestats = TRUE;

    } else if(strncmp(string, "-Xprofiling:", 12) == 0) {
        // 不知道是啥
        args->profile_threshold = strtol(string + 12, NULL, 0);

    } else if(strncmp(string, "-Xreplication:", 14) == 0) {
        // TODO 复制、重复？
        char *pntr = string + 14;

        if(strcmp(pntr, "none") == 0)
            args->replication_threshold = INT_MAX;
        else
            if(strcmp(pntr, "always") == 0)
                args->replication_threshold = 0;
            else
                args->replication_threshold = strtol(pntr, NULL, 0);

    } else if(strncmp(string, "-Xcodemem:", 10) == 0) {
        // TODO 代码内存？？干嘛用的
        char *pntr = string + 10;

        args->codemem = strncmp(pntr, "unlimited", 10) == 0 ?
            INT_MAX : parseMemValue(pntr);

    } else if(strcmp(string, "-Xshowreloc") == 0) {
        // 展示搬迁？ 
        // TODO 这个方法调用是干嘛的？
        showRelocatability();
#endif

#ifdef HAVE_PROFILE_STUBS
    } else if(strcmp(string, "-Xdumpstubsprofiles") == 0) {
        args->dump_stubs_profiles = TRUE;
#endif
    /* Compatibility options 兼容性选项 */
    } else {
        // TODO 这段代码块是干嘛的？好像会控制status 达到什么效果？
        int i;

        for(i = 0; compat[i].option != NULL; i++) {
            int len = strlen(compat[i].option);

            if(strncmp(string, compat[i].option, len) == 0 &&
               (((compat[i].flags & OPT_ARG) && string[len] == ':') ||
                ((compat[i].flags & OPT_NOARG) && string[len] == '\0')))
                break;
        }

        if(compat[i].option == NULL)
            status = OPT_UNREC;
    }
       
    return status;
}
